package online.filter.helper;

import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.Objects;

import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * レスポンス複製クラス
 *
 * @author Tadashi Nakayama
 * @version 1.0.0
 */
public final class DuplicateBodyResponse implements Serializable {
	/** serialVersionUID */
	private static final long serialVersionUID = 6876402783979599742L;

	/** レスポンスオブジェクト複製クラス */
	private final DuplicateHeaderResponse dhr;

	/** キャラクタ配列ライタ */
	private DuplicateCharArrayWriter charWriter = null;
	/** 出力ストリーム */
	private DuplicateServletOutputStream dow = null;

	/** リクエストURL */
	private String requestUrl = null;
	/** トークン */
	private String token = null;

	/**
	 * コンストラクタ
	 * @param header DuplicateHeaderResponse
	 */
	public DuplicateBodyResponse(final DuplicateHeaderResponse header) {
		this.dhr = header;
	}

	/**
	 * @return the requestUrl
	 */
	public String getRequestUrl() {
		return this.requestUrl;
	}

	/**
	 * @param val the requestUrl to set
	 */
	public void setRequestUrl(final String val) {
		this.requestUrl = val;
	}

	/**
	 * @param request HttpServletRequest
	 * @return the token
	 */
	public boolean isSameToken(final HttpServletRequest request) {
		return Objects.equals(this.token, request.getParameter("TOKEN"));
	}

	/**
	 * @param request HttpServletRequest
	 */
	public void setToken(final HttpServletRequest request) {
		this.token = String.class.cast(request.getAttribute("TOKEN"));
	}

	/**
	 * ヘッダ取得
	 * @return ヘッダ
	 */
	public DuplicateHeaderResponse getHeader() {
		return this.dhr;
	}

	/**
	 * 出力ストリーム取得
	 *
	 * @return 出力ストリーム
	 */
	public ServletOutputStream getOutputStream() {
		if (this.charWriter != null) {
			throw new java.lang.IllegalStateException(
					"getWriter() has already been called for this object");
		}

		if (this.dow == null) {
			this.dow = new DuplicateServletOutputStream(this.dhr.getCharacterEncoding());
		}

		return this.dow;
	}

	/**
	 * ライタ取得
	 *
	 * @return ライタオブジェクト
	 */
	public PrintWriter getWriter() {
		if (this.dow != null) {
			throw new java.lang.IllegalStateException(
				"getOutputStream() has already been called for this object");
		}

		if (this.charWriter == null) {
			this.charWriter = new DuplicateCharArrayWriter();
		}

		return new PrintWriter(this.charWriter);
	}

	/**
	 * リセットバッファ
	 */
	public void resetBuffer() {
		this.charWriter = null;
		this.dow = null;
	}

	/**
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return Objects.toString(this.charWriter, "");
	}

	/**
	 * レスポンス複製処理
	 *
	 * @param response 複製対象レスポンス
	 * @throws IOException IO例外
	 */
	public void copyResponse(final HttpServletResponse response) throws IOException {
		if (response != null) {
			this.dhr.copyResponse(response);

			// キャラクタ配列ライタ
			if (this.charWriter != null) {
				response.getWriter().write(this.charWriter.toString());
			}
			// 出力ストリーム
			if (this.dow != null) {
				response.getOutputStream().print(this.dow.toString());
			}
		}
	}

	/**
	 * リクエスト確認
	 * @param location 位置
	 * @param request リクエスト
	 * @return 同一の場合 true を返す。
	 */
	public boolean isSameRequest(final String location, final HttpServletRequest request) {
		return location.equals(getRequestUrl()) && isSameToken(request);
	}

	/**
	 * 出力ストリームラッパ
	 *
	 * @author Tadashi Nakayama
	 * @version 1.0.0
	 */
	private static final class DuplicateServletOutputStream
					extends ServletOutputStream implements Serializable {
		/** serialVersionUID */
		private static final long serialVersionUID = 6870980097315758174L;

		/** バイト配列 */
		private byte[] bytes = null;
		/** カウンタ */
		private int count = 0;

		/** Charset名 */
		private final String charset;

		/**
		 * コンストラクタ
		 * @param val Charset名
		 */
		public DuplicateServletOutputStream(final String val) {
			this.charset = val;
		}

		/**
		 * @see java.io.OutputStream#write(int)
		 */
		@Override
		public void write(final int b) throws IOException {
			int newcount = this.count + 1;
			if (this.bytes.length < newcount) {
				this.bytes = Arrays.copyOf(this.bytes, Math.max(this.bytes.length * 2, newcount));
			}
			this.bytes[this.count] = (byte)b;
			this.count = newcount;
		}

		/**
		 * @see java.lang.Object#toString()
		 */
		@Override
		public String toString() {
			try {
				return new String(this.bytes, 0, this.count, this.charset);
			} catch (final UnsupportedEncodingException e) {
				return "";
			}
		}

		/**
		 * @see javax.servlet.ServletOutputStream#isReady()
		 */
		@Override
		public boolean isReady() {
			return false;
		}

		/**
		 * @see javax.servlet.ServletOutputStream#setWriteListener(javax.servlet.WriteListener)
		 */
		@Override
		public void setWriteListener(final WriteListener arg0) {
			return;
		}
	}

	/**
	 * 複製ライタ
	 *
	 * @author Tadashi Nakayama
	 * @version 1.0.0
	 */
	private static final class DuplicateCharArrayWriter
					extends CharArrayWriter implements Serializable {
		/** serialVersionUID */
		private static final long serialVersionUID = -1692537205581508939L;

		/**
		 * コンストラクタ
		 */
		public DuplicateCharArrayWriter() {
			super();
		}
	}
}
